package edu.jhu.cs.oose.scheduleit.services.impl;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import edu.jhu.cs.oose.scheduleit.dao.GroupDao;
import edu.jhu.cs.oose.scheduleit.dao.ScheduleDao;
import edu.jhu.cs.oose.scheduleit.dao.UserDao;
import edu.jhu.cs.oose.scheduleit.services.ScheduleService;
import edu.jhu.cs.oose.scheduleit.util.Attribute;
import edu.jhu.cs.oose.scheduleit.util.Filter;
import edu.jhu.cs.oose.scheduleit.util.GroupAttributeKind;
import edu.jhu.cs.oose.scheduleit.util.JoinRule;
import edu.jhu.cs.oose.scheduleit.util.JoinStatus;
import edu.jhu.cs.oose.scheduleit.util.ScheduleType;
import edu.jhu.cs.oose.scheduleit.vo.Group;
import edu.jhu.cs.oose.scheduleit.vo.Schedule;
import edu.jhu.cs.oose.scheduleit.vo.User;
import edu.jhu.cs.oose.scheduleit.vo.UserInfo;
/**
 * The implementation class of the GroupService interface
 * @author oosegroup2
 *
 */
public class GroupServiceImpl implements
		edu.jhu.cs.oose.scheduleit.services.GroupService {
	/**
	 * The data access object related to Group
	 */
	private GroupDao groupDao;
	/**
	 * The data access object related to Schedule
	 */
	private ScheduleDao scheduleDao;
	/**
	 * The data access object related to User
	 */
	private UserDao userDao;
	/**
	 * The service for schedule
	 */
	private ScheduleService scheduleService;

	
	public void setGroupDao(GroupDao groupDao) {
		this.groupDao = groupDao;
	}

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public void setScheduleDao(ScheduleDao scheduleDao) {
		this.scheduleDao = scheduleDao;
	}

	public void setScheduleService(ScheduleService scheduleService) {
		this.scheduleService = scheduleService;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Group createGroup(User user, Attribute attribute)
			throws DataBaseException {
		try {
			// Create Group
			Group group = new Group();
			group.setHost(user.getUserInfo());
			Object value = attribute.getValue(GroupAttributeKind.topic);
			if (value != null) {
				group.setTopic((String) value);
			} else {
				group.setTopic("");
			}

			value = attribute.getValue(GroupAttributeKind.description);
			if (value != null) {
				group.setDescription((String) value);
			} else {
				group.setDescription("");
			}
			
			value = attribute.getValue(GroupAttributeKind.tags);
			List<String> tags = new LinkedList<String>();
			if (value != null){
				tags.addAll((Collection<String>)value);
			}
			group.setTags(tags);
			
			value = attribute.getValue(GroupAttributeKind.clicks);
			if (value != null) {
				if (value instanceof Integer) {
					group.setClicks((Integer) value);
				} else {
					group.setClicks((Long) value);
				}
			}else{
				group.setClicks(0);
			}
			
			value = attribute.getValue(GroupAttributeKind.joinRule);
			if (value != null) {
				group.setJoinRule((JoinRule)value);
			} else {
				group.setJoinRule(JoinRule.PUBLIC);
			}
			
			this.groupDao.save(group);

			// Create Schedule
			Schedule schedule = new Schedule();
			schedule.setHost(user.getUserInfo());
			schedule.setGroup(group);
			schedule.setClicks(0);
			schedule.setEditable(true);
			schedule.setPublicAccess(false);
			this.scheduleDao.save(schedule);
			group.setSchedule(schedule);
			this.groupDao.save(group);
			// Set Relationship
			this.groupDao.addUser(group, user, JoinStatus.JOINED);
			this.userDao.addGroup(user, group, JoinStatus.JOINED);
			return group;
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public List<Group> getGroup(List<Filter> filters, int offest, int limit)
			throws DataBaseException {
		try {
			return this.groupDao.getGroup(filters, offest, limit);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public int getGroupCount(List<Filter> filters)
			throws DataBaseException {
		try {
			return this.groupDao.countGroup(filters);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public Group getGroup(int groupID) throws DataBaseException {
		try {
			return this.groupDao.getGroup(groupID);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public List<UserInfo> getUser(Group group, JoinStatus status, int offset,
			int limit) throws DataBaseException {
		try {
			return this.groupDao.getUser(group, status, offset, limit);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public int getUserCount(Group group, JoinStatus status)
			throws DataBaseException {
		try {
			return this.groupDao.countUser(group, status);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public JoinStatus joinGroup(Group group, User user)
			throws DataBaseException {
		try {
			if (this.isDenied(group, user)) {
				return JoinStatus.DENIED;
			} else if (this.isJoined(group, user)) {
				return JoinStatus.JOINED;
			} else if (this.isApplied(group, user)) {
				return JoinStatus.APPLIED;
			} else {
				JoinStatus status = group.getJoinRule() == JoinRule.PUBLIC ? JoinStatus.JOINED
						: JoinStatus.APPLIED;
				this.groupDao.addUser(group, user, status);
				this.userDao.addGroup(user, group, status);
				return status;
			}
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public void quitGroup(Group group, User user) throws DataBaseException {
		try {
			if (group.getHost().equals(user.getUserInfo())) {
				List<UserInfo> users = this.getUser(group, JoinStatus.APPLIED,
						0, 0);
				for (UserInfo ui : users) {
					User tmpUser = this.userDao.get(ui.getEmail());
					this.groupDao
							.deleteUser(group, tmpUser, JoinStatus.APPLIED);
					this.userDao
							.deleteGroup(group, tmpUser, JoinStatus.APPLIED);
				}
				users = this.getUser(group, JoinStatus.JOINED, 0, 0);
				for (UserInfo ui : users) {
					User tmpUser = this.userDao.get(ui.getEmail());
					this.groupDao.deleteUser(group, tmpUser, JoinStatus.JOINED);
					this.userDao.deleteGroup(group, tmpUser, JoinStatus.JOINED);
				}
				users = this.getUser(group, JoinStatus.DENIED, 0, 0);
				for (UserInfo ui : users) {
					User tmpUser = this.userDao.get(ui.getEmail());
					this.groupDao.deleteUser(group, tmpUser, JoinStatus.DENIED);
					this.userDao
							.deleteGroup(group, tmpUser, JoinStatus.APPLIED);
				}
				group.getSchedule().setType(ScheduleType.GENERAL);
				this.scheduleService.removeSchedule(group.getSchedule());
				this.groupDao.deleteGroup(group);
			} else {
				if (this.isJoined(group, user)) {
					this.groupDao.deleteUser(group, user, JoinStatus.JOINED);
					this.userDao.deleteGroup(group, user, JoinStatus.JOINED);
				} else if (this.isApplied(group, user)) {
					this.groupDao.deleteUser(group, user, JoinStatus.APPLIED);
					this.userDao.deleteGroup(group, user, JoinStatus.APPLIED);
				} else if (this.isDenied(group, user)) {
					this.userDao.deleteGroup(group, user, JoinStatus.APPLIED);
				}
			}
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public void updateGroup(Group group) throws DataBaseException {
		try {
			this.groupDao.save(group);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public boolean isJoined(Group group, User user) throws DataBaseException {
		try {
			return this.groupDao.isJoined(group, user);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public boolean isApplied(Group group, User user) throws DataBaseException {
		try {
			return this.groupDao.isApplied(group, user);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public boolean isDenied(Group group, User user) throws DataBaseException {
		try {
			return this.groupDao.isDenied(group, user);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

	@Override
	public void accept(Group group, User user) throws DataBaseException {
		try {
			this.groupDao.deleteUser(group, user, JoinStatus.APPLIED);
			this.userDao.deleteGroup(group, user, JoinStatus.APPLIED);
			this.groupDao.addUser(group, user, JoinStatus.JOINED);
			this.userDao.addGroup(user, group, JoinStatus.JOINED);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}

	}

	@Override
	public void reject(Group group, User user) throws DataBaseException {
		try {
			this.groupDao.deleteUser(group, user, JoinStatus.APPLIED);
			this.groupDao.addUser(group, user, JoinStatus.DENIED);
		} catch (Exception e) {
			throw new DataBaseException(e);
		}
	}

}
